/*
 * Eugen Stoian <stoian.e@gmail.com> 2010
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "BinaryOpNode.h"
#include "UnaryOpNode.h"
#include "ValueNode.h"
#include "OpADDNode.h"
#include "OpSUBNode.h"
#include "OpMULNode.h"
#include "OpDIVNode.h"
#include "OpPOWNode.h"
#include "OpEQNode.h"
#include "OpNENode.h"
#include "OpLTNode.h"
#include "OpLENode.h"
#include "OpGTNode.h"
#include "OpGENode.h"
#include "OpORNode.h"
#include "OpANDNode.h"
#include "OpIFNode.h"
#include "OpELSENode.h"

#include <cmath>


/**
 *\fn BinaryOpNode::BinaryOpNode(const QString& info, MathBinaryFun fun, ExpressionTreeOp derivativeBuilder)
 *\brief Constructor
 */
BinaryOpNode::BinaryOpNode(const QString& info, MathBinaryFun fun, ExpressionTreeOp derivativeBuilder)
        :_left(0),_right(0),_op(fun),_derivativeBuilder(derivativeBuilder),_info(info) {
}
/**
 *\fn BinaryOpNode::~BinaryOpNode()
 *\brief Destructor
 */
BinaryOpNode::~BinaryOpNode() {
}



/**
 *\fn Real BinaryOpNode::eval() const
 *\brief Recursively evaluates contained math expression
 *
 *
 */
Real BinaryOpNode::eval() const {
    if(_left && _right && _op){
        Real r = (*_op)(_left->eval(),_right->eval());
        _marker = _left->marker() + _right->marker();
        return r;
    }
    return Real::nan();
}
/**
 *\fn SmartPtr<ExpressionTreeNode> BinaryOpNode::derive(const QString& varName) const
 *\brief Returns the tree associated to the symbolic math derivative of the contained math expression
 *\param varName Name of the variable with respect to which the derivative is computed
 *
 */
SmartPtr<ExpressionTreeNode> BinaryOpNode::derive(const QString& varName) const {
    if(_derivativeBuilder)
        return (*_derivativeBuilder)(this->copy(),varName);
    return 0;
}

/**
 *\fn void BinaryOpNode::bind(int varPos, Real * var) const
 *\brief
 *\param varName
 *\param var
 *
 */
void BinaryOpNode::bind(int varPos,  const Real * var) const{
    if(_left && _right){
        _left->bind(varPos,var);
        _right->bind(varPos,var);
    }
}
/**
 *\fn void BinaryOpNode::bind(const QString& varName, Real * var) const
 *\brief
 *\param varName
 *\param var
 *
 */
void BinaryOpNode::bind(const QString& varName,  const Real * var) const{
    if(_left && _right){
        _left->bind(varName,var);
        _right->bind(varName,var);
    }
}
/**
 *\fn void BinaryOpNode::replace(const QString& varName, ExpressionTreeNode * tree) const
 *\brief
 *\param varName
 *\param tree
 *
 */
ExpressionTreeNode* BinaryOpNode::replace(const QString& varName, ExpressionTreeNode* tree) const {
    if(!tree)
        return 0;
    SmartPtr<ExpressionTreeNode> left;
    SmartPtr<ExpressionTreeNode> right;
    ExpressionTreeNode* root = 0;
    if(_left && _right){
        left = _left->replace(varName, tree);
        right = _right->replace(varName, tree);
        root = makeNew(info());
        root->setLeft(left);
        root->setRight(right);
    }
    return root;
}

/**
 *\fn ExpressionTreeNode* BinaryOpNode::replace(int varPos, ExpressionTreeNode* tree) const
 *\brief
 *\param varPos
 *\param tree
 *
 */
ExpressionTreeNode* BinaryOpNode::replace(int varPos, ExpressionTreeNode* tree) const {
    if(!tree)
        return 0;
    SmartPtr<ExpressionTreeNode> left;
    SmartPtr<ExpressionTreeNode> right;
    ExpressionTreeNode* root = 0;
    if(_left && _right){
        left = _left->replace(varPos, tree);
        right = _right->replace(varPos, tree);
        root = makeNew(info());
        root->setLeft(left);
        root->setRight(right);
    }
    return root;
}



/**
 *\fn QString BinaryOpNode::toPrefixString() const
 *\brief
 *
 */
QString BinaryOpNode::toPrefixString() const {
    if(_left && _right) {
        return _info + " " +_left->toPrefixString() + " " + _right->toPrefixString();
    }
    return "";

}

/**
 *\fn QString BinaryOpNode::toPostfixString() const
 *\brief
 *
 */
QString BinaryOpNode::toPostfixString() const {
    if(_left && _right) {
        return _left->toPostfixString()+ " " +_right->toPostfixString()+ " " +_info;
    }
    return "";
}

/**
 *\fn QString BinaryOpNode::toInfixString() const
 *\brief
 *
 */
QString BinaryOpNode::toInfixString() const {
    QString left;
    QString right;
    if(_left && _right) {
        if(_left->isBinaryOp() && (_left->precedence() < precedence()))
            left = "("+_left->toInfixString()+")";
        else 
            left = _left->toInfixString();

        if(_right->isBinaryOp() && (_right->precedence() < precedence()))
            right = "("+_right->toInfixString()+")";
        else
            right = _right->toInfixString();
        
        if((_info == "/")||(_info == "^")){
            if(_right->isOperand())
                return "("+left+_info+right+")";
            else
                return "("+left+_info+"("+right+"))";
        }
        return left+_info+right;
    }
    return "";
}

ExpressionTreeNode* BinaryOpNode::makeNew(const QString& info) {
    if(info==ADD_SYMBOL){
        return new OpADDNode();
    }
    if(info==SUB_SYMBOL){
        return new OpSUBNode();
    }
    if(info==MUL_SYMBOL){
        return new OpMULNode();
    }
    if(info==DIV_SYMBOL){
        return new OpDIVNode();
    }
    if(info==POW_SYMBOL){
        return new OpPOWNode();
    }
    if(info==AND_SYMBOL){
        return new OpANDNode();
    }
    if(info==OR_SYMBOL){
        return new OpORNode();
    }
    if(info==IF_SYMBOL){
        return new OpIFNode();
    }
    if(info==ELSE_SYMBOL){
        return new OpELSENode();
    }
    if(info==EQ_SYMBOL){
        return new OpEQNode();
    }
    if(info==NE_SYMBOL){
        return new OpNENode();
    }
    if(info==LE_SYMBOL){
        return new OpLENode();
    }
    if(info==LT_SYMBOL){
        return new OpLTNode();
    }
    if(info==GT_SYMBOL){
        return new OpGTNode();
    }
    if(info==GE_SYMBOL){
        return new OpGENode();
    }
    return 0;
}

void BinaryOpNode::resetMarker() const{
    _left->resetMarker();
    _right->resetMarker();
    _marker = QString();
}
